package com.yql.commonextend.utils;

import cn.hutool.json.JSONUtil;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @Author: chenjun
 * @Description: 签名算法类
 * @Date: 2021/11/12
 **/
public class SignatureUtil {

    // 全局数组
    private final static String[] strDigits = { "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

    // 返回形式为数字跟字符串
    private static String byteToArrayString(byte bByte) {
        int iRet = bByte;
        if (iRet < 0) {
            iRet += 256;
        }
        int iD1 = iRet / 16;
        int iD2 = iRet % 16;
        return strDigits[iD1] + strDigits[iD2];
    }

    // 转换字节数组为16进制字串
    private static String byteToString(byte[] bByte) {
        StringBuffer sBuffer = new StringBuffer();
        for (int i = 0; i < bByte.length; i++) {
            sBuffer.append(byteToArrayString(bByte[i]));
        }
        return sBuffer.toString();
    }

    /**
     * 参数生成加密的签名串，排序
     * @param map
     * @return
     */
    public static String signature(Map<String, Object> map) {
        return getMD5Code(getSign(map, true));
    }

    /**
     * 参数生成加密的签名串，不排序
     * @param map
     * @return
     */
    public static String signatureNotSort(Map<String, Object> map) {
        return getMD5Code(getSign(map, false));
    }

    /**
     * 验签
     * @param sign  签名
     * @param map  参数
     * @return
     */
    public static boolean verifySign(String sign , Map<String, Object> map) {
        if(sign == null){
            return false;
        }
        if(sign.equals(getMD5Code(getSign(map, true)))){
            return true;
        }
        return false;
    }

    /**
     * 验签
     * @param sign  签名
     * @param map  参数
     * @return
     */
    public static boolean verifySignNotSort(String sign , Map<String, Object> map) {
        if(sign == null){
            return false;
        }
        if(sign.equals(getMD5Code(getSign(map, false)))){
            return true;
        }
        return false;
    }

    /**
     * 参数生成加密的签名串，按LinkedHashMap默认排序
     * @param map
     * @param key 加盐
     * @return
     */
    public static String signatureByLinkedHashMap(LinkedHashMap<String, Object> map, String key) {
        return getMD5Code(getSignNotOrder(map) + key);
    }

    public static String getMD5Code(String strObj) {
        String resultString = null;
        try {
            resultString = new String(strObj);
            MessageDigest md = MessageDigest.getInstance("MD5");
            // md.digest() 该函数返回值为存放哈希值结果的byte数组
            resultString = byteToString(md.digest(strObj.getBytes()));
        } catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
        }
        return resultString;
    }

    /**
     * 默认排序，传入的map需是LinkedHashMap
     * @param map
     * @return
     */
    private static String getSignNotOrder(LinkedHashMap<String, Object> map) {
        StringBuilder sb = new StringBuilder();
        for (String str : map.keySet()) {
            sb.append(map.get(str));
        }
        if(sb.length()==0){
            return "";
        }
        return sb.toString();
    }

    /**
     * 进行排序（默认排序）
     * @param map
     * @param sort 是否按照字段名的ASCII码从小到大排序
     * @return
     */
    public static String getSign(Map<String, Object> map, boolean sort) {
        List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(map.entrySet());
         //对所有传入参数按照字段名的ASCII码从小到大排序（字典序）
        if(sort) {
            Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {
                @Override
                public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                    return (o1.getKey().toString().compareTo(o2.getKey()));
                }
            });
        }
        // 构造签名键值对的格式
        StringBuilder sb = new StringBuilder();
        int size = map.size();
        int length = 0;
        for (Map.Entry<String, Object> item : infoIds) {
            if (item.getKey() != null || item.getKey() != "") {
                length++;
                String key = item.getKey();
                Object val = item.getValue();
                sb.append(key + "=" + (val == null ? "" : val) + (length == size ? "" : "&"));
            }
        }
        if(sb.length() == 0){
            return "";
        }
        return sb.toString();
    }
}
